package LocalStorage

import (
	"KumquatDrivesServices/StorageEngine"
	"KumquatDrivesServices/model"
	"KumquatDrivesServices/utils"
	"fmt"
	"github.com/sirupsen/logrus"
	"io"
	"os"
	"path/filepath"
	"strconv"
	"strings"
)

type sortableFile struct {
	entry os.DirEntry
	index int // 文件名中的索引值
}

func (s sortableFiles) toDirEntry() []os.DirEntry {
	var entries []os.DirEntry
	for _, sortableFiled := range s {
		entries = append(entries, sortableFiled.entry)
	}
	return entries
}

type sortableFiles []sortableFile

// quickSort 是快速排序的递归函数
func (s sortableFiles) quickSort(left, right int) {
	if left >= right {
		return
	}
	p := s.partition(left, right)
	s.quickSort(left, p-1)
	s.quickSort(p+1, right)
}

// partition 是快速排序的分区函数
func (s sortableFiles) partition(left, right int) int {
	pivot := s[right].index
	i := left - 1
	for j := left; j < right; j++ {
		if s[j].index <= pivot {
			i++
			s[i], s[j] = s[j], s[i]
		}
	}
	s[i+1], s[right] = s[right], s[i+1]
	return i + 1
}

// Sort 使用快速排序对 sortableFiles 进行排序
func (s sortableFiles) Sort() {
	if len(s) <= 1 {
		return
	}
	s.quickSort(0, len(s)-1)
}

// prepareSortableFiles 准备可排序的文件列表，并解析文件名中的索引值
func prepareSortableFiles(entries []os.DirEntry) (sortableFiles, error) {
	var files sortableFiles

	for _, entry := range entries {
		if !entry.IsDir() && strings.HasSuffix(entry.Name(), ext) {
			// 去掉扩展名，得到可能的索引字符串
			nameWithoutExt := strings.TrimSuffix(entry.Name(), ext)

			// 尝试将文件名转换为整数索引
			index, err := strconv.Atoi(nameWithoutExt)
			if err != nil {
				// 如果文件名不是有效的整数，可以跳过或返回错误
				logrus.Errorf("Warning: file %q has non-integer name, skipping.\n", entry.Name())
				continue // 或者使用 return nil, err 来立即返回错误
			}
			files = append(files, sortableFile{entry: entry, index: index})
		}
	}
	return files, nil
}

func (Le LocalEngine) MergeTempFiles(uploadId StorageEngine.EngineID, userId model.SnowflakeIdType, destFilename string, fileSize int64) (string, int64, error) {
	bol, IsDiscardErr := uploadId.IsDiscard(userId)
	if IsDiscardErr != nil {
		logrus.Errorf("uploadId.IsDiscard(%d) err: %s", userId, IsDiscardErr)
		return "", 0, IsDiscardErr
	}
	if bol {
		if err := Le.DelTempFile(uploadId); err != nil {
			logrus.Errorf("Le.DelTempFile(%d) err: %s", uploadId, err)
		}
		return "", 0, StorageEngine.InvalidEngineIDErr
	}
	var userModel model.UserModel
	userModel.ID = userId
	tmpPath := Le.getTmpFilePath(uploadId)
	dirEntries, err := os.ReadDir(tmpPath)
	if err != nil {
		logrus.Errorf("Warning: cannot read temp files in upload %s.\n", err)
		return "", 0, err
	}
	sortFiles, SortErr := prepareSortableFiles(dirEntries)
	if SortErr != nil {
		logrus.Errorf("Warning: cannot prepare sortable files in upload %s.\n", SortErr)
		return "", 0, SortErr
	}
	sortFiles.Sort()
	files := sortFiles.toDirEntry()
	info, GetCacheUserInfoErr := userModel.GetCacheUserInfo()
	if GetCacheUserInfoErr != nil {
		logrus.Errorf("Warning: cannot get user info from upload %s.\n", GetCacheUserInfoErr)
		return "", 0, err
	}
	distFilePath := filepath.Join(Le.getFilePath(), info.UserEmail, destFilename)
	if err = utils.CreatePath(distFilePath); err != nil {
		return "", 0, err
	}
	out, CreateErr := os.Create(distFilePath)
	if CreateErr != nil {
		logrus.Errorf("Warning: cannot create temp file in upload %s.\n", CreateErr)
		return "", 0, CreateErr
	}
	defer out.Close()
	var srcFile *os.File
	for _, entry := range files {
		srcPath := filepath.Join(tmpPath, entry.Name())
		var srcErr error
		srcFile, srcErr = os.Open(srcPath)
		if srcErr != nil {
			logrus.Errorf("Warning: cannot open temp file in upload %s.\n", srcErr)
			return "", 0, srcErr
		}
		_, CopyErr := io.Copy(out, srcFile)
		if CopyErr != nil {
			logrus.Errorf("Warning: cannot copy temp file in upload %s.\n", CopyErr)
			return "", 0, CopyErr
		}
		srcFile.Close()
	}
	file, fileErr := os.Stat(distFilePath)
	if fileErr != nil {
		logrus.Errorf("Warning: cannot stat temp file in upload %s.\n", fileErr)
		return "", 0, fileErr
	}
	if file.Size() != fileSize {
		return "", 0, fmt.Errorf("文件损坏")
	}
	_ = Le.DelTempFile(uploadId)
	logrus.Info(file.Size())
	return distFilePath, file.Size(), nil
}
